import { ByteView, CodecType } from "./codec";
import { make, cmd, type } from "./proto";
import { serialPort } from "./SerialPort";


export class Frame {

    private readonly buf: number[];

    readonly type: number;
    readonly seq: number;

    constructor(buf: number[]) {
        this.buf = buf;
        this.type = buf[0];
        this.seq = buf[1];
    }

    id() {
        return (this.type << 8) + this.seq;
    }

    cmd() {
        return this.buf[2];
    }

    parse_notify(args: CodecType[]) {
        let index = 3;
        for (const arg of args) {
            arg.decode(this.buf, index);
            index += arg.size();
        }
    }

    parse_res(args: CodecType[]) {
        const ret = this.buf[3];
        if (ret != 0) {
            throw `exec fail:${ret}`;
        }

        let index = 4;
        for (const arg of args) {
            arg.decode(this.buf, index);
            index += arg.size();
        }
    }
}

const frameHandlerMap = new Map<number, (f: Frame) => void>();
let logCb = (_log: string) => {};

export function registerHandler(id: number, fun: (f: Frame) => void) {
    frameHandlerMap.set(id, fun);
}

export function setLogCb(cb: (log: string) => void) {
    logCb = cb;
}

function onNotify(frame: Frame) {

    switch (frame.cmd()) {
        case cmd.STATUS:

        case cmd.LOG:
            const buf = new ByteView();
            frame.parse_notify([buf]);
            logCb(buf.toString());
        break;
    }
}

export function recv(buf: number[]) {
    const frame = new Frame(buf);
    if ((frame.type == type.NOTIFY_ACK) || (frame.type == type.RES)) {
        serialPort.write( make(type.ACK, frame.seq, []) );
    }
    switch (frame.type) {
        case type.PONG:
        case type.RES:
        case type.ACK:
        case type.SIMPLE_RES:
            const func = frameHandlerMap.get( frame.id() );
            if (func != null) {
                func(frame);
            }
            break;
        
        case type.NOTIFY:     
        case type.NOTIFY_ACK:
            onNotify(frame);
        break;

        default:
            console.error('invalid type frame', frame);
            break;
    }
}
